Composing builds简介

在Android Studio项目中,经常会引用多个Module,而且会有多人同时参与项目开发,在这种背景下,会时常遇到版本冲突问题,出现不同的compileSdkVersion或者出现同一个库的多个不同版本等,导致我们的包体变大,项目运行时间变长,所以将依赖版本统一是一个项目优化的必经之路。

到目前为止Google为管理Gradle依赖提供了4种不同方法:

  • 手动管理

    在每个module中定义插件依赖库,每次升级依赖库时都需要手动更改(不建议使用)。

  • ext的方式管理

    这是Google推荐管理依赖的方法 Android官方文档。但是无法跟踪依赖关系,可读性差,不便维护。

  • Kotlin + buildSrc

    支持自动补全和单击跳转,依赖更新时将重新构建整个项目。

  • Composing builds

    自动补全和单击跳转,依赖更新时不会重新构建整个项目。

Groovy ext扩展函数的替代方式

我们在使用Groovy语言构建项目的时候,抽取config.gradle作为全局的变量控制,使用ext扩展函数来统一配置依赖,如下:

Image

ext {
    android = [
            compileSdkVersion: 29,
            buildToolsVersion: "29",
            minSdkVersion    : 17,
            targetSdkVersion : 26,
            versionCode      : 102,
            versionName      : "1.0.2"
    ]

    version = [
            appcompatVersion       : "1.1.0",
            coreKtxVersion         : "1.2.0",
            supportLibraryVersion  : "28.0.0",
            androidTestVersion     : "3.0.1",
            junitVersion           : "4.12",
            glideVersion           : "4.11.0",
            okhttpVersion          : "3.11.0",
            retrofitVersion        : "2.3.0",
            constraintLayoutVersion: "1.1.3",
            gsonVersion            : "2.7",
            rxjavaVersion          : "2.2.2",
            rxandroidVersion       : "2.1.0",
            ..........省略...........
    ]

    dependencies = [
            //base
            "constraintLayout"      : "androidx.constraintlayout:constraintlayout:${version["constraintLayoutVersion"]}",
            "appcompat"             : "androidx.appcompat:appcompat:${version["appcompatVersion"]}",
            "coreKtx"               : "androidx.core:core-ktx:${version["coreKtxVersion"]}",
            "material"              : "com.google.android.material:material:1.2.1",

            //multidex
            "multidex"              : "com.android.support:multidex:${version["multidexVersion"]}",

            //okhttp
            "okhttp"                : "com.squareup.okhttp3:okhttp:${version["okhttpVersion"]}",
            "logging-interceptor"   : "com.squareup.okhttp3:logging-interceptor:${version["okhttpVersion"]}",

            //retrofit
            "retrofit"              : "com.squareup.retrofit2:retrofit:${version["retrofitVersion"]}",
            "converter-gson"        : "com.squareup.retrofit2:converter-gson:${version["retrofitVersion"]}",
            "adapter-rxjava2"       : "com.squareup.retrofit2:adapter-rxjava2:${version["retrofitVersion"]}",
            "converter-scalars"     : "com.squareup.retrofit2:converter-scalars:${version["retrofitVersion"]}",
            ..........省略...........
    ]
}

依赖写完之后,在root路径下的build.gradle添加以下代码:

apply from: "config.gradle"

然后在需要依赖的module下的build.gradle中:

dependencies {
    ...
    // Retrofit + okhttp 相关的依赖包
    api rootProject.ext.dependencies["retrofit"]
    ...
}

以上就是Groovy ext扩展函数的依赖管理方式,此方式可以做到版本依赖,但是最大的缺点就是无法跟踪代码,想要找到上面示例代码中的rootProject.ext.dependencies["retrofit"]这个依赖,需要手动切到config.gradle去搜索查找,可读性很差。

buildSrc+kotlin

Android Gradle插件4.0支持在Gradle构建配置中使用Kotlin脚本 (KTS),用于替代Groovy(过去在Gradle配置文件中使用的编程语言)。

将来,KTS会比Groovy更适合用于编写Gradle脚本,因为采用Kotlin编写的代码可读性更高,并且Kotlin提供了更好的编译时检查和IDE支持。

虽然与Groovy相比,KTS当前能更好地在Android Studio的代码编辑器中集成,但采用KTS的构建速度往往比采用Groovy慢,因此在迁移到KTS时应考虑构建性能。

KTS:是指Kotlin脚本,这是Gradle在构建配置文件中使用的一种Kotlin语言形式。Kotlin脚本是可从命令行运行的Kotlin代码。

Kotlin DSL:主要是指Android Gradle插件Kotlin DSL,有时也指底层Gradle Kotlin DSL

摘自 Gradle 文档:当运行Gradle时会检查根项目中是否存在一个名为buildSrc的目录,该目录包含了项目build相关的逻辑。然后Gradle会自动编译并测试这段代码,并将其放入构建脚本的类路径中, 对于多项目构建,只能有一个buildSrc目录,该目录必须位于根项目目录中,buildSrc是Gradle项目根目录下的一个目录,它可以包含我们的构建逻辑,与脚本插件相比,buildSrc应该是首选,因为它更易于维护、重构和测试代码。

buildSrc的方式,是最近几年特别流行的版本依赖管理方式。它有以下几个优点:

  • 支持双向跟踪
  • buildSrc是Android默认插件,全局只有这一个地方可以修改
  • 支持Android Studio的代码补全

使用方式可参考:Kotlin + buildSrc for Better Gradle Dependency Management

  • 在项目根目录下新建一个名为buildSrc的文件夹( 名字必须是buildSrc,因为运行Gradle时会检查项目中是否存在一个名为buildSrc的目录 )
  • 在buildSrc文件夹里创建名为build.gradle.kts的文件,添加以下内容:
plugins {
    `kotlin-dsl`
}
repositories{
    jcenter
}
  • 在buildSrc/src/main/java/包名/目录下新建Deps.kt文件,添加以下内容:
object Versions {
    val appcompat = "1.1.0"
}

object Deps {
    val appcompat =  "androidx.appcompat:appcompat:${Versions.appcompat}"
}
  • 重启Android Studio,项目里就会多出一个名为buildSrc的module。

缺点:

  • buildSrc 依赖更新将重新构建整个项目,项目越大,重新构建的时间就越长,造成不必要的时间浪费。

脚本文件命名

  • 用Groovy编写的Gradle build文件使用.gradle文件扩展名。
  • 用Kotlin编写的Gradle build文件使用.gradle.kts文件扩展名。

常见误区

用于定义字符串的双引号。Groovy允许使用单引号来定义字符串,而Kotlin则要求使用双引号。

基于句点表达式的字符串插值。在Groovy中,您可以使用$前缀来表示基于句点表达式的字符串插值,例如以下代码段中的$project.rootDir:

myRootDirectory = "$project.rootDir/tools/proguard-rules-debug.pro"

但在Kotlin中,上述代码将对 project(而非 project.rootDir)调用 toString()。如需获取根目录的值,请使用大括号括住整个变量:

  • myRootDirectory = "${project.rootDir}/tools/proguard-rules-debug.pro"
  • 变量分配。一些在Groovy中适用的分配方式现在会被视作setter(或者,对于列表、集合等,则适用“addX”),因为属性在Kotlin中是只读的。

显式和隐式buildTypes

在Kotlin DSL中,某些buildTypes(如debug和release)是隐式提供的。但是,其他buildTypes则必须手动创建。

例如,在Groovy中,您可能有debug、release和staging buildTypes:

Groovy

buildTypes debug {  ... } release {  ... } staging {  ... }

在KTS中,仅debug和release buildTypes是隐式提供的,而staging则必须由您手动创建:

KTS

buildTypes getByName("debug") {  ... } getByName("release") {  ... } create("staging") {  ... }

使用plugins代码块

如果您在build文件中使用plugins代码块,IDE将能够获知相关上下文信息,即使在构建失败时也是如此。IDE可使用这些信息执行代码补全并提供其他实用建议,从而帮助您解决KTS文件中存在的问题。

在您的代码中,将命令式apply plugin替换为声明式plugins代码块。Groovy中的以下代码:

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
apply plugin: 'androidx.navigation.safeargs.kotlin'

…在 KTS 中变为以下代码:

plugins {  
    id("com.android.application")  
    id("kotlin-android")  
    id("kotlin-kapt")  
    id("androidx.navigation.safeargs.kotlin") 
}

如需详细了解plugins代码块,请参阅 Gradle 的迁移指南

Composing builds

Image

Composing builds:A composite build is simply a build that includes other builds. In many ways a composite build is similar to a Gradle multi-project build, except that instead of including single projects, complete builds are included. 复合构建只是包含其他构建的构建. 在许多方面,复合构建类似于Gradle多项目构建,不同之处在于,它包括完整的builds,而不是包含单个projects:

  • 组合通常独立开发的构建,例如,在应用程序使用的库中尝试错误修复时
  • 将大型的多项目构建分解为更小,更孤立的块,可以根据需要独立或一起工作

Image

使用方式

  1. 新建module,名为versionPlugin(自起)
  2. 在该module下的build.gradle文件中,添加如下代码:
buildscript {
    ext.kotlin_version = "1.5.10"
    repositories {
        mavenCentral()
    }
    dependencies {
        // 因为使用的Kotlin需要需要添加Kotlin插件
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    }
}

apply plugin: 'kotlin'
apply plugin: 'java-gradle-plugin'

repositories {
    mavenCentral()
}

compileKotlin {
    kotlinOptions {
        jvmTarget = "1.8"
    }
}
compileTestKotlin {
    kotlinOptions {
        jvmTarget = "1.8"
    }
}

gradlePlugin {
    plugins {
        version {
            // 在app模块需要通过id引用这个插件
            id = 'com.xx.xx.plugin'
            // 实现这个插件的类的路径
            implementationClass = 'com.xx.xx.versionplugin.Deps'
        }
    }
}
  1. 在versionPlugin/src/main/java/包名/目录下新建Deps.kt文件,添加你的依赖配置,如:
package com.xx.xx.versionplugin

class Deps : Plugin<Project> {
    override fun apply(project: Project) {
        // Possibly common dependencies or can stay empty
    }

    companion object {
        val appcompat = "androidx.appcompat:appcompat:1.1.0"
    }
}

或者也可以按依赖类型用不同的类配置,例如

object CustomLibs {
    ...
        object Glide {
        private const val glideVersion = "4.11.0"
        const val glide = "com.github.bumptech.glide:glide:$glideVersion"
        const val glideCompiler = "com.github.bumptech.glide:compiler:$glideVersion"
    }

    object Retrofit {
        private const val retrofitVersion = "2.9.0"
        const val retrofit = "com.squareup.retrofit2:retrofit:$retrofitVersion"
        const val converter_gson = "com.squareup.retrofit2:converter-gson:$retrofitVersion"
    }
}
  1. 在settings.gradle文件内添加includeBuild 'versionPlugin',注意是includeBuild哦~,Rebuild项目

  2. 后面就可以在需要使用的gradle文件中使用了,在app或其他module下的build.gradle,在首行添加以下内容:

plugins {
    id 'com.android.application'
    id 'kotlin-android'
    id 'kotlin-kapt'
    // 通过id来使用该plugin,这个id就是在versionPlugin文件夹下build.gradle文件内定义的id
    id 'com.xx.xx.plugin'
}

使用如下:

dependencies {
    implementation CustomLibs.Glide.glide
    kapt CustomLibs.Glide.glideCompiler
}

参考


results matching ""

    No results matching ""